import xID from "./xID.js";

const tmplBase = `
        <div  class="{{class}}">
             <div type="head" class="xhead">   
                  <input class="xinput"/>
             </div>
             <div type="main" style="display:none"  class="xmain">
                 <ul type="ul">
                 </ul>
             </div>
        </div>`;

const tmplLi= '<li>{{item}}</li>'
const tmplItem ='<span type="data">{{data}}</span>'
const tmplTitle = 'title="{{title}}"';

export default class zInput {
    /* 定义变量 开始*/
    // jquery 对象

    $root;
    $head;
    $main;
    $ul;
    $input;
    vBindEvent;
    vCfg;
    vInputValue;
    vData;
    vTmpEvent={};
    vClickId = 'click.'+xID.getID();
    vCtxId = 'contextmenu.'+xID.getID();
    vDropActive=-1;
    /* 定义变量 结束*/

    constructor(cfg) {
        this.$root = $('#' + cfg.el);
        this.vInputValue = cfg.active;
        this.$root.html(tmplBase.replace('{{class}}', cfg.style||'zinput'));
        this.vCfg = cfg;
        this.init();
    };

    /* 渲染区块 开始 */
    init() {
        this.$head = this.$root.find('[type="head"]');
        this.$main = this.$root.find('[type="main"]');
        this.$ul = this.$root.find('[type="ul"]');
        this.$input = this.$root.find('input');
        this.$input.on('focus',this.evFocus.bind(this));
        this.$input.on('input',this.evInput.bind(this));
        this.$input.on('keydown',this.evKeydown.bind(this));
        this.$ul.on('click','[type="data"]',this.evDataClick.bind(this));
        this.$head.on('click',this.evActive.bind(this));
        this.$input.on('keydown',this.evKeydown.bind(this));
        this.$root.on('click', this.evRootClick.bind(this));
        if(this.vCfg.placeholder){
            this.$input.attr('placeholder',this.vCfg.placeholder);
        }
        let title ='';
        if(this.vCfg.title)
            title = tmplTitle.replace("{{title}}",this.vCfg.title);
        this.$head.attr("title",title);

        if(this.vCfg.active)
            this.initValue(this.vCfg.active);

        if(this.vCfg.readonly)
            this.setReadonly(true);

    };

    /*
  总共 4 个状态
    active  ,无 active
    valid,无valid ,invalid

 */
    renderStatus(action,status){
        if(action=='add'){
            this.$head.addClass(status);
        }else{
            this.$head.removeClass(status);
        }
    }

    initValue(value){
        if(this.vInputValue != value){
            this.vInputValue = value;
            this.$input.val(value);
            this.verify();
        }
    }

    renderUl(){
        let lis='';
        let mData = this.matchData();
        if(mData && mData.length>0){
            for(let data of mData){
                lis += tmplLi.replace('{{item}}',this.renderItem(data));
            }
        }
        return lis;
    };

    renderItem(data){
        return tmplItem.replace('{{data}}',data)
    }

    mainShow(){
        this.vDropActive = -1;
        let ul= this.renderUl();
        if(ul){
            this.$ul.html(ul);
            this.$main.show();
        }
    }

    evFocus(e){
        this.evActive(e);
    }

    evActive(e){
        if(this.vCfg.readonly)
            return;
        if(this.$head.hasClass("active"))
            return;
        this.renderStatus("add",'active');
        $(document).on(this.vClickId,this.evOff.bind(this));
        $(document).on(this.vCtxId,this.evCtx.bind(this));
        this.getDataAndShow();
    }

    evInput(e){
        let change = true;
        if(this.vCfg.pattern){
            let RegVal = e.target.value.replace(this.vCfg.pattern,'');
            //没有被替换掉说明 不满足正则表达式 需要回退
            if(RegVal == e.target.value && e.target.value && this.vCfg.inputVerify){
                e.target.value= this.vInputValue;
                change = false;
            }else{
                this.vInputValue = e.target.value;
            }
        }else{
            this.vInputValue = e.target.value;
            this.mainShow();
        }
        if(change && this.vBindEvent)
            this.vBindEvent(this.vInputValue);
    }

    evKeydown(e){
        let keynum;
        keynum = window.event ? e.keyCode : e.which;
        switch(keynum){
            //enter
            case 13:
                this.$main.hide();
                break;
                //up
            case 38:
                //down
            case 40:
                this.activeItem(keynum);
                break;
            case 9:
                this.evOff.bind(this)(e);
                break;
            // esc
            case 27:
                this.$main.hide();
                break;
        }
    }

    activeItem(keynum){
        let all = this.$ul.find('[type="data"]');
        //let index;
        let oldIndex =-1;
        if(this.vDropActive<0){
            this.vDropActive = keynum==38?all.length-1:0
        }
        else{
            oldIndex = this.vDropActive;
            this.vDropActive = keynum==38?this.vDropActive-1:this.vDropActive+1;
            this.vDropActive =this.vDropActive%all.length;
            if(this.vDropActive<0)
                this.vDropActive = all.length+this.vDropActive;
        }

        if(oldIndex>-1){
            $(all[oldIndex]).removeClass("active");
        }
        $(all[this.vDropActive]).addClass("active");
        this.vInputValue = $(all[this.vDropActive]).html();
        this.$input.val(this.vInputValue)
        if(this.vBindEvent)
            this.vBindEvent(this.vInputValue)
    }

    evDataClick(e){
        let data =e.currentTarget.innerHTML;
        this.$input.val(data);
        this.vInputValue = data;
        this.$main.hide();
        if(this.vBindEvent)
            this.vBindEvent(this.vInputValue);
    }

    evRootClick(e){
        this.vTmpEvent.timeStamp = e.timeStamp;
        this.vTmpEvent.target =e .target;
    };

    evCtx(e){
         //this.$root.find(e.target)
        if(this.$root.find(e.target).length<1){
            this.evOff(e)
        }
    }

    evOff(e){
        if(e.type=='keydown' || e.type=="contextmenu"||
            (e.type=='click' && (this.vTmpEvent.timeStamp !=e.timeStamp || this.vTmpEvent.target !=e.target))){
            this.storeData();
            this.renderStatus('remove','active');
            this.verify();
            this.$main.hide();
            $(document).unbind(this.vClickId);
            $(document).unbind(this.vCtxId);

        }
    }

    storeData(){
        if(this.vInputValue && this.vCfg.key){
            //回调函数
            if(this.vCfg.store)
                this.vCfg.store(this.vCfg.key,this.vInputValue)
            else{
                let sData = localStorage.getItem(this.vCfg.key)
                if(sData)
                    sData = this.vInputValue+','+sData;
                else
                    sData = this.vInputValue;
                if(sData){
                    let set = new Set(sData.split(','));
                    //最多30 个
                    let keyArray = Array.from(set);
                    keyArray = keyArray.slice(keyArray.length>30?keyArray.length-30:0);
                    localStorage.setItem(this.vCfg.key,keyArray.join(','));
                }
            }
        }
    }

    getDataAndShow(){
        if(this.vCfg.get)
            this.vCfg.get(this.vCfg.key).then(data => {
                this.vData = data;
                this.mainShow();
            } )
        else{
            let sData = localStorage.getItem(this.vCfg.key);
            if(sData){
                this.vData = sData.split(',');
                this.mainShow();
            }

        }
    }

    matchData(){
        if(this.vInputValue){
           let data = [] ;
           if(this.vData){
               for(let d of this.vData){
                   if((d+'').match(this.vInputValue))
                       data.push(d);
               }
           }
           return data;
        }
        else
            return this.vData;
    }

    setReadonly(status){
        this.vCfg.readonly = status;
        if(status){
            this.renderStatus("add","readonly");
            this.$input.attr("readonly","readonly")
        }else{
            this.renderStatus("remove","readonly");
            this.$input.removeAttr("readonly")
        }
    }

    setBindEvent(event){
        this.vBindEvent = event;
    };

    setValue(value){
        this.initValue(value);
    };

    verify() {
        let result = true;
        if(this.vCfg.mandatory ){
            if(!this.vInputValue ){
                this.renderStatus("remove",'valid');
                this.renderStatus("add","invalid");
                result =false
            }else {
                this.renderStatus("remove",'invalid');
                this.renderStatus("add","valid");
            }
        }
        return result;
    }

    getValue(){
        return this.vInputValue;
    }

}